using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using HISModels.Entities;
using HISModels.Mapping;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Linq;
using System.Data.Objects;

namespace HISModels {
    public class HISContext : DbContext {
        static HISContext() {
            //Database.SetInitializer<HISContext>(null);
            Database.SetInitializer<HISContext>(new CreateDatabaseIfNotExists<HISContext>());
        }

        public HISContext(string conString)
            : base(conString) {
        }

        public DbSet<Address> Addresses { get; set; }
        public DbSet<AddressType> AddressTypes { get; set; }
        public DbSet<Alergy> Alergies { get; set; }
        public DbSet<Appointment> Appointments { get; set; }
        public DbSet<Bank> Banks { get; set; }
        public DbSet<BillDetail> BillDetails { get; set; }
        public DbSet<Bill> Bills { get; set; }
        public DbSet<CorporatePayer> CorporatePayers { get; set; }
        public DbSet<Departement> Departements { get; set; }
        public DbSet<Diet> Diets { get; set; }
        public DbSet<DocRegRefRel> DocRegRefRels { get; set; }
        public DbSet<DoctorExt> DoctorExts { get; set; }
        public DbSet<DoctorHuRel> DoctorHuRels { get; set; }
        public DbSet<DoctorReceipt> DoctorReceipts { get; set; }
        public DbSet<DoctorRegRel> DoctorRegRels { get; set; }
        public DbSet<Doctor> Doctors { get; set; }
        public DbSet<DoctorSchedule> DoctorSchedules { get; set; }
        public DbSet<DoctorSpecRel> DoctorSpecRels { get; set; }
        public DbSet<Employee> Employees { get; set; }
        public DbSet<EpisodeDoctorRel> EpisodeDoctorRels { get; set; }
        public DbSet<Episode> Episodes { get; set; }
        public DbSet<GoodReturn> GoodReturns { get; set; }
        public DbSet<GrnDetail> GrnDetails { get; set; }
        public DbSet<GrnMaster> GrnMasters { get; set; }
        public DbSet<GSCSBillRel> GSCSBillRels { get; set; }
        public DbSet<GSCSlipDetail> GSCSlipDetails { get; set; }
        public DbSet<GSCSlipPriceListRel> GSCSlipPriceListRels { get; set; }
        public DbSet<GSCSlip> GSCSlips { get; set; }
        public DbSet<Habit> Habits { get; set; }
        public DbSet<HuBed> HuBeds { get; set; }
        public DbSet<HuLobRel> HuLobRels { get; set; }
        public DbSet<HuRoom> HuRooms { get; set; }
        public DbSet<HuWard> HuWards { get; set; }
        public DbSet<Insurance> Insurances { get; set; }
        public DbSet<ItemForm> ItemForms { get; set; }
        public DbSet<ItemLocation> ItemLocations { get; set; }
        public DbSet<Item> Items { get; set; }
        public DbSet<ItemSite> ItemSites { get; set; }
        public DbSet<ItemStock> ItemStocks { get; set; }
        public DbSet<ItemSupplierRel> ItemSupplierRels { get; set; }
        public DbSet<Laboratiory> Laboratiories { get; set; }
        public DbSet<MedicalFamily> MedicalFamilies { get; set; }
        public DbSet<MedicalHistories> MedicalHistories { get; set; }
        public DbSet<MedicalOccupation> MedicalOccupation { get; set; }
        public DbSet<MedicationActual> MedicationActuals { get; set; }
        public DbSet<MedicationPlan> MedicationPlans { get; set; }
        public DbSet<MedicationReceip> MedicationReceips { get; set; }
        public DbSet<MemberApplication> MemberApplications { get; set; }
        public DbSet<MemberEmployee> MemberEmployees { get; set; }
        public DbSet<PatientPassId> PatientPassIds { get; set; }
        public DbSet<PatientPayer> PatientPayers { get; set; }
        public DbSet<PatientReg> PatientRegs { get; set; }
        public DbSet<PatientRelative> PatientRelatives { get; set; }
        public DbSet<Patient> Patients { get; set; }
        public DbSet<PhysicianNote> PhysicianNotes { get; set; }
        public DbSet<PoDetail> PoDetails { get; set; }
        public DbSet<PoMaster> PoMasters { get; set; }
        public DbSet<Position> Positions { get; set; }
        public DbSet<PriceList> PriceLists { get; set; }
        public DbSet<ProblemList> ProblemLists { get; set; }
        public DbSet<ProductionDetail> ProductionDetails { get; set; }
        public DbSet<Production> Productions { get; set; }
        public DbSet<PurchaseRequsitionDetail> PurchaseRequsitionDetails { get; set; }
        public DbSet<PurchaseRequsition> PurchaseRequsitions { get; set; }
        public DbSet<Radiology> Radiologies { get; set; }
        public DbSet<ReceiptDetail> ReceiptDetails { get; set; }
        public DbSet<RefArea> RefAreas { get; set; }
        public DbSet<RefCity> RefCities { get; set; }
        public DbSet<RefCountry> RefCountries { get; set; }
        public DbSet<RefDayOfWeek> RefDayOfWeeks { get; set; }
        public DbSet<RefDoctorAction> RefDoctorActions { get; set; }
        public DbSet<RefDocumentStatus> RefDocumentStatuses { get; set; }
        public DbSet<RefDocumentType> RefDocumentTypes { get; set; }
        public DbSet<RefEducation> RefEducations { get; set; }
        public DbSet<RefEthnicity> RefEthnicities { get; set; }
        public DbSet<RefFamilyRelativeType> RefFamilyRelativeTypes { get; set; }
        public DbSet<RefGender> RefGenders { get; set; }
        public DbSet<RefHu> RefHus { get; set; }
        public DbSet<RefIdentityCardType> RefIdentityCardTypes { get; set; }
        public DbSet<RefInstitutionType> RefInstitutionTypes { get; set; }
        public DbSet<RefItemCategory> RefItemCategories { get; set; }
        public DbSet<RefItemDrugType> RefItemDrugTypes { get; set; }
        public DbSet<RefItemShape> RefItemShapes { get; set; }
        public DbSet<RefItemSubCategory> RefItemSubCategories { get; set; }
        public DbSet<RefItemType> RefItemTypes { get; set; }
        public DbSet<RefLobPackage> RefLobPackages { get; set; }
        public DbSet<RefLob> RefLobs { get; set; }
        public DbSet<RefLos> RefLoses { get; set; }
        public DbSet<RefMarital> RefMaritals { get; set; }
        public DbSet<RefMedicalVocab> RefMedicalVocabs { get; set; }
        public DbSet<RefOccupation> RefOccupations { get; set; }
        public DbSet<RefOthRelativeType> RefOthRelativeTypes { get; set; }
        public DbSet<RefPayerCategory> RefPayerCategories { get; set; }
        public DbSet<RefProvince> RefProvinces { get; set; }
        public DbSet<RefReligion> RefReligions { get; set; }
        public DbSet<RefSalutation> RefSalutations { get; set; }
        public DbSet<RefSpecialist> RefSpecialists { get; set; }
        public DbSet<RefSpecialistGroup> RefSpecialistGroups { get; set; }
        public DbSet<RefSubSpecialist> RefSubSpecialists { get; set; }
        public DbSet<RefSupplierType> RefSupplierTypes { get; set; }
        public DbSet<RefType> RefTypes { get; set; }
        public DbSet<RefUomType> RefUomTypes { get; set; }
        public DbSet<Relative> Relatives { get; set; }
        public DbSet<Supplier> Suppliers { get; set; }
        public DbSet<SynonimTag> SynonimTags { get; set; }
        public DbSet<SysActionGroup> SysActionGroups { get; set; }
        public DbSet<SysAction> SysActions { get; set; }
        public DbSet<sysdiagram> sysdiagrams { get; set; }
        public DbSet<SysFileCategory> SysFileCategories { get; set; }
        public DbSet<SysFileUpload> SysFileUploads { get; set; }
        public DbSet<SysLog> SysLogs { get; set; }
        public DbSet<SysMembership> SysMemberships { get; set; }
        public DbSet<SysNavigationGroup> SysNavigationGroups { get; set; }
        public DbSet<SysNavigation> SysNavigations { get; set; }
        public DbSet<SysOptionGroup> SysOptionGroups { get; set; }
        public DbSet<SysOption> SysOptions { get; set; }
        public DbSet<SysRoleAction> SysRoleActions { get; set; }
        public DbSet<SysRole> SysRoles { get; set; }
        public DbSet<SysSequenceNumber> SysSequenceNumbers { get; set; }
        public DbSet<VitalSign> VitalSigns { get; set; }
        public DbSet<WorkflowApproval> WorkflowApprovals { get; set; }
        public DbSet<WorkflowApprovalType> WorkflowApprovalTypes { get; set; }
        public DbSet<WorkflowMasterTemplate> WorkflowMasterTemplates { get; set; }
        public DbSet<Workflow> Workflows { get; set; }
        public DbSet<WorkflowStatus> WorkflowStatuses { get; set; }
        public DbSet<WorkflowTemplate> WorkflowTemplates { get; set; }
        public DbSet<Worklist> Worklists { get; set; }
        public DbSet<VDoctor> VDoctors { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
            modelBuilder.Configurations.Add(new AddressMap());
            modelBuilder.Configurations.Add(new AddressTypeMap());
            modelBuilder.Configurations.Add(new AlergyMap());
            modelBuilder.Configurations.Add(new AppointmentMap());
            modelBuilder.Configurations.Add(new BankMap());
            modelBuilder.Configurations.Add(new BillDetailMap());
            modelBuilder.Configurations.Add(new BillMap());
            modelBuilder.Configurations.Add(new CorporatePayerMap());
            modelBuilder.Configurations.Add(new DepartementMap());
            modelBuilder.Configurations.Add(new DietMap());
            modelBuilder.Configurations.Add(new DocRegRefRelMap());
            modelBuilder.Configurations.Add(new DoctorExtMap());
            modelBuilder.Configurations.Add(new DoctorHuRelMap());
            modelBuilder.Configurations.Add(new DoctorReceiptMap());
            modelBuilder.Configurations.Add(new DoctorRegRelMap());
            modelBuilder.Configurations.Add(new DoctorMap());
            modelBuilder.Configurations.Add(new DoctorScheduleMap());
            modelBuilder.Configurations.Add(new DoctorSpecRelMap());
            modelBuilder.Configurations.Add(new EmployeeMap());
            modelBuilder.Configurations.Add(new EpisodeDoctorRelMap());
            modelBuilder.Configurations.Add(new EpisodeMap());
            modelBuilder.Configurations.Add(new GoodReturnMap());
            modelBuilder.Configurations.Add(new GrnDetailMap());
            modelBuilder.Configurations.Add(new GrnMasterMap());
            modelBuilder.Configurations.Add(new GSCSBillRelMap());
            modelBuilder.Configurations.Add(new GSCSlipDetailMap());
            modelBuilder.Configurations.Add(new GSCSlipPriceListRelMap());
            modelBuilder.Configurations.Add(new GSCSlipMap());
            modelBuilder.Configurations.Add(new HabitMap());
            modelBuilder.Configurations.Add(new HuBedMap());
            modelBuilder.Configurations.Add(new HuLobRelMap());
            modelBuilder.Configurations.Add(new HuRoomMap());
            modelBuilder.Configurations.Add(new HuWardMap());
            modelBuilder.Configurations.Add(new InsuranceMap());
            modelBuilder.Configurations.Add(new ItemFormMap());
            modelBuilder.Configurations.Add(new ItemLocationMap());
            modelBuilder.Configurations.Add(new ItemMap());
            modelBuilder.Configurations.Add(new ItemSiteMap());
            modelBuilder.Configurations.Add(new ItemStockMap());
            modelBuilder.Configurations.Add(new ItemSupplierRelMap());
            modelBuilder.Configurations.Add(new LaboratioryMap());
            modelBuilder.Configurations.Add(new MedicalFamilyMap());
            modelBuilder.Configurations.Add(new MedicalHistoriesMap());
            modelBuilder.Configurations.Add(new MedicalOccupationMap());
            modelBuilder.Configurations.Add(new MedicationActualMap());
            modelBuilder.Configurations.Add(new MedicationPlanMap());
            modelBuilder.Configurations.Add(new MedicationReceipMap());
            modelBuilder.Configurations.Add(new MemberApplicationMap());
            modelBuilder.Configurations.Add(new MemberEmployeeMap());
            modelBuilder.Configurations.Add(new PatientPassIdMap());
            modelBuilder.Configurations.Add(new PatientPayerMap());
            modelBuilder.Configurations.Add(new PatientRegMap());
            modelBuilder.Configurations.Add(new PatientRelativeMap());
            modelBuilder.Configurations.Add(new PatientMap());
            modelBuilder.Configurations.Add(new PhysicianNoteMap());
            modelBuilder.Configurations.Add(new PoDetailMap());
            modelBuilder.Configurations.Add(new PoMasterMap());
            modelBuilder.Configurations.Add(new PositionMap());
            modelBuilder.Configurations.Add(new PriceListMap());
            modelBuilder.Configurations.Add(new ProblemListMap());
            modelBuilder.Configurations.Add(new ProductionDetailMap());
            modelBuilder.Configurations.Add(new ProductionMap());
            modelBuilder.Configurations.Add(new PurchaseRequsitionDetailMap());
            modelBuilder.Configurations.Add(new PurchaseRequsitionMap());
            modelBuilder.Configurations.Add(new RadiologyMap());
            modelBuilder.Configurations.Add(new ReceiptDetailMap());
            modelBuilder.Configurations.Add(new RefAreaMap());
            modelBuilder.Configurations.Add(new RefCityMap());
            modelBuilder.Configurations.Add(new RefCountryMap());
            modelBuilder.Configurations.Add(new RefDayOfWeekMap());
            modelBuilder.Configurations.Add(new RefDoctorActionMap());
            modelBuilder.Configurations.Add(new RefDocumentStatusMap());
            modelBuilder.Configurations.Add(new RefDocumentTypeMap());
            modelBuilder.Configurations.Add(new RefEducationMap());
            modelBuilder.Configurations.Add(new RefEthnicityMap());
            modelBuilder.Configurations.Add(new RefFamilyRelativeTypeMap());
            modelBuilder.Configurations.Add(new RefGenderMap());
            modelBuilder.Configurations.Add(new RefHuMap());
            modelBuilder.Configurations.Add(new RefIdentityCardTypeMap());
            modelBuilder.Configurations.Add(new RefInstitutionTypeMap());
            modelBuilder.Configurations.Add(new RefItemCategoryMap());
            modelBuilder.Configurations.Add(new RefItemDrugTypeMap());
            modelBuilder.Configurations.Add(new RefItemShapeMap());
            modelBuilder.Configurations.Add(new RefItemSubCategoryMap());
            modelBuilder.Configurations.Add(new RefItemTypeMap());
            modelBuilder.Configurations.Add(new RefLobPackageMap());
            modelBuilder.Configurations.Add(new RefLobMap());
            modelBuilder.Configurations.Add(new RefLosMap());
            modelBuilder.Configurations.Add(new RefMaritalMap());
            modelBuilder.Configurations.Add(new RefMedicalVocabMap());
            modelBuilder.Configurations.Add(new RefOccupationMap());
            modelBuilder.Configurations.Add(new RefOthRelativeTypeMap());
            modelBuilder.Configurations.Add(new RefPayerCategoryMap());
            modelBuilder.Configurations.Add(new RefProvinceMap());
            modelBuilder.Configurations.Add(new RefReligionMap());
            modelBuilder.Configurations.Add(new RefSalutationMap());
            modelBuilder.Configurations.Add(new RefSpecialistMap());
            modelBuilder.Configurations.Add(new RefSpecialistGroupMap());
            modelBuilder.Configurations.Add(new RefSubSpecialistMap());
            modelBuilder.Configurations.Add(new RefSupplierTypeMap());
            modelBuilder.Configurations.Add(new RefTypeMap());
            modelBuilder.Configurations.Add(new RefUomTypeMap());
            modelBuilder.Configurations.Add(new RelativeMap());
            modelBuilder.Configurations.Add(new SupplierMap());
            modelBuilder.Configurations.Add(new SynonimTagMap());
            modelBuilder.Configurations.Add(new SysActionGroupMap());
            modelBuilder.Configurations.Add(new SysActionMap());
            modelBuilder.Configurations.Add(new sysdiagramMap());
            modelBuilder.Configurations.Add(new SysFileCategoryMap());
            modelBuilder.Configurations.Add(new SysFileUploadMap());
            modelBuilder.Configurations.Add(new SysLogMap());
            modelBuilder.Configurations.Add(new SysMembershipMap());
            modelBuilder.Configurations.Add(new SysNavigationGroupMap());
            modelBuilder.Configurations.Add(new SysNavigationMap());
            modelBuilder.Configurations.Add(new SysOptionGroupMap());
            modelBuilder.Configurations.Add(new SysOptionMap());
            modelBuilder.Configurations.Add(new SysRoleActionMap());
            modelBuilder.Configurations.Add(new SysRoleMap());
            modelBuilder.Configurations.Add(new SysSequenceNumberMap());
            modelBuilder.Configurations.Add(new VitalSignMap());
            modelBuilder.Configurations.Add(new WorkflowApprovalMap());
            modelBuilder.Configurations.Add(new WorkflowApprovalTypeMap());
            modelBuilder.Configurations.Add(new WorkflowMasterTemplateMap());
            modelBuilder.Configurations.Add(new WorkflowMap());
            modelBuilder.Configurations.Add(new WorkflowStatusMap());
            modelBuilder.Configurations.Add(new WorkflowTemplateMap());
            modelBuilder.Configurations.Add(new WorklistMap());
            modelBuilder.Configurations.Add(new VDoctorMap());
        }

        public int SaveChanges(string userId) {
            try {
                // Get all Added/Deleted/Modified entities (not Unmodified or Detached)
                foreach (var ent in this.ChangeTracker.Entries().Where(p => p.State == System.Data.EntityState.Added || p.State == System.Data.EntityState.Deleted || p.State == System.Data.EntityState.Modified)) {
                    // For each changed record, get the audit record entries and add them
                    foreach (SysLog x in GetAuditRecordsForChange(ent, userId)) {
                        this.SysLogs.Add(x);
                    }
                }
            } catch { }

            // Call the original SaveChanges(), which will save both the changes made and the audit records
            return base.SaveChanges();
        }
        private string GetPrimaryKeyName(DbEntityEntry entry) {
            var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
            return objectStateEntry.EntitySet.ElementType.KeyMembers.Single().Name;
        }

        private List<SysLog> GetAuditRecordsForChange(DbEntityEntry dbEntry, string userId) {
            List<SysLog> result = new List<SysLog>();

            DateTime changeTime = DateTime.UtcNow;
            TableAttribute tableAttr = null;
            string tableName = string.Empty;
            string keyName = string.Empty;

            try {
                // Get the Table() attribute, if one exists
                tableAttr = dbEntry.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;

                // Get table name (if it has a Table attribute, use that, otherwise get the pluralized name)
                tableName = tableAttr != null ? tableAttr.Name : dbEntry.Entity.GetType().Name;

                // Get primary key value (If you have more than one key column, this will need to be adjusted)
                keyName = GetPrimaryKeyName(dbEntry);


                if (dbEntry.State == System.Data.EntityState.Added) {
                    // For Inserts, just add the whole record
                    result.Add(new SysLog() {
                        UserID = userId,
                        EventDateUTC = changeTime,
                        EventType = "A", // Added
                        TableName = tableName,
                        RecordID = dbEntry.CurrentValues.GetValue<object>(keyName).ToString(),
                        ColumnName = "*ALL",
                        CurrentValue = DescribeEntity(dbEntry.CurrentValues.ToObject())
                    }
                        );
                } else if (dbEntry.State == System.Data.EntityState.Deleted) {
                    // Same with deletes, do the whole record
                    result.Add(new SysLog() {
                        UserID = userId,
                        EventDateUTC = changeTime,
                        EventType = "D", // Deleted
                        TableName = tableName,
                        RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
                        ColumnName = "*ALL",
                        BeforeValue = DescribeEntity(dbEntry.OriginalValues.ToObject())
                    }
                        );
                } else if (dbEntry.State == System.Data.EntityState.Modified) {
                    foreach (string propertyName in dbEntry.OriginalValues.PropertyNames) {
                        // For updates, we only want to capture the columns that actually changed
                        if (!object.Equals(dbEntry.GetDatabaseValues().GetValue<object>(propertyName), dbEntry.CurrentValues.GetValue<object>(propertyName))) {
                            result.Add(new SysLog() {
                                UserID = userId,
                                EventDateUTC = changeTime,
                                EventType = "M",    // Modified
                                TableName = tableName,
                                RecordID = dbEntry.OriginalValues.GetValue<object>(keyName).ToString(),
                                ColumnName = propertyName,
                                BeforeValue = dbEntry.GetDatabaseValues().GetValue<object>(propertyName) == null ? null : dbEntry.GetDatabaseValues().GetValue<object>(propertyName).ToString(),
                                CurrentValue = dbEntry.CurrentValues.GetValue<object>(propertyName) == null ? null : dbEntry.CurrentValues.GetValue<object>(propertyName).ToString()
                            }
                                );
                        }
                    }
                }
            } catch { }

            return result;
        }


        private string DescribeEntity(object obj) {
            string returnValue = string.Empty;
            returnValue = "{";
            var properties = from p in obj.GetType().GetProperties()
                             where p.CanRead && p.CanWrite
                             select p;
            foreach (var property in properties) {
                var value = property.GetValue(obj, null) == null ? string.Empty : property.GetValue(obj, null).ToString();
                returnValue += property.Name + ":" + "\"" + value + "\" , ";

            }
            returnValue += "}";
            return returnValue;
        }

    }
}

